home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CUCD / Programming / SWI / source / src / pl-os.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-05-18  |  56.9 KB  |  2,709 lines

  1. /*  pl-os.c,v 1.45 1994/04/11 08:37:37 jan Exp
  2.  
  3.     Copyright (c) 1990 Jan Wielemaker. All rights reserved.
  4.     See ../LICENCE to find out about your rights.
  5.     jan@swi.psy.uva.nl
  6.  
  7.     Purpose: Operating System Dependencies
  8. */
  9.  
  10. /*  Modified (M) 1993 Dave Sherratt  */
  11.  
  12. /*#define O_DEBUG 1*/
  13.  
  14. #define unix 1
  15.  
  16. #if __TOS__
  17. #include <tos.h>        /* before pl-os.h due to Fopen, ... */
  18. #endif
  19. #if OS2 && EMX
  20. #include <os2.h>                /* this has to appear before pl-incl.h */
  21. #endif
  22.  
  23. #include <math.h>            /* avoid abs() problem with msvc++ */
  24. #include "pl-incl.h"
  25. #include "pl-ctype.h"
  26.  
  27. #if HAVE_SYS_STAT_H
  28. #include <sys/stat.h>
  29. #endif
  30. #if !O_XOS
  31. #define statfunc stat
  32. #endif
  33. #if HAVE_PWD_H
  34. #include <pwd.h>
  35. #endif
  36. #if HAVE_VFORK_H
  37. #include <vfork.h>
  38. #endif
  39. #ifdef HAVE_UNISTD_H
  40. #include <unistd.h>
  41. #endif
  42. #ifdef HAVE_SYS_PARAM_H
  43. #include <sys/param.h>
  44. #endif
  45. #ifdef HAVE_SYS_FILE_H
  46. #include <sys/file.h>
  47. #endif
  48.  
  49. #include <fcntl.h>
  50. #ifndef __WATCOMC__            /* appears a conflict */
  51. #include <errno.h>
  52. #endif
  53.  
  54. #if defined(__WATCOMC__)
  55. #include <io.h>
  56. #include <dos.h>
  57. #endif
  58.  
  59. #if OS2 && EMX
  60. static real initial_time;
  61. #endif /* OS2 */
  62.  
  63. forwards void    initExpand(void);
  64. forwards void    initRandom(void);
  65. forwards void    initEnviron(void);
  66. forwards long    Time(void);
  67. static void    RemoveTemporaryFiles(void);
  68.  
  69. #ifndef DEFAULT_PATH
  70. #define DEFAULT_PATH "/bin:/usr/bin"
  71. #endif
  72.  
  73.          /*******************************
  74.          *           GLOBALS        *
  75.          *******************************/
  76. #ifdef HAVE_CLOCK
  77. long clock_wait_ticks;
  78. #endif
  79.  
  80. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  81. This module is a contraction of functions that used to be all  over  the
  82. place.   together  with  pl-os.h  (included  by  pl-incl.h) this file
  83. should define a basic  layer  around  the  OS,  on  which  the  rest  of
  84. SWI-Prolog  is  based.   SWI-Prolog  has  been developed on SUN, running
  85. SunOs 3.4 and later 4.0.
  86.  
  87. Unfortunately some OS's simply do not offer  an  equivalent  to  SUN  os
  88. features.   In  most  cases part of the functionality of the system will
  89. have to be dropped. See the header of pl-incl.h for details.
  90. - - - - - - - - - - -  - - - - - */
  91.  
  92.         /********************************
  93.         *         INITIALISATION        *
  94.         *********************************/
  95.  
  96. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  97.     bool initOs()
  98.  
  99.     Initialise the OS dependant functions.
  100. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  101.  
  102. bool
  103. initOs(void)
  104. { DEBUG(1, Sdprintf("OS:initExpand() ...\n"));
  105.   initExpand();
  106.   DEBUG(1, Sdprintf("OS:initRandom() ...\n"));
  107.   initRandom();
  108.   DEBUG(1, Sdprintf("OS:initEnviron() ...\n"));
  109.   initEnviron();
  110.  
  111. #ifdef __WIN32__
  112.   if ( iswin32s() )
  113.     set(&features, DOS_FILE_NAMES_FEATURE);
  114.   else
  115.     set(&features, FILE_CASE_PRESERVING_FEATURE);
  116. #else
  117.   set(&features, FILE_CASE_FEATURE);
  118.   set(&features, FILE_CASE_PRESERVING_FEATURE);
  119. #endif
  120.  
  121. #ifdef HAVE_CLOCK
  122.   clock_wait_ticks = 0L;
  123. #endif
  124.  
  125. #if OS2
  126.   { DATETIME i;
  127.     DosGetDateTime((PDATETIME)&i);
  128.     initial_time = (i.hours * 3600.0) 
  129.                    + (i.minutes * 60.0) 
  130.            + i.seconds
  131.            + (i.hundredths / 100.0);
  132.   }
  133. #endif /* OS2 */
  134.  
  135.   DEBUG(1, Sdprintf("OS:done\n"));
  136.  
  137.   succeed;
  138. }
  139.  
  140. typedef void (*halt_function)(int, Void);
  141.  
  142. struct on_halt
  143. { halt_function    function;
  144.   Void        argument;
  145.   OnHalt    next;
  146. };
  147.  
  148.  
  149. void
  150. PL_on_halt(halt_function f, Void arg)
  151. { if ( !GD->os.halting )
  152.   { OnHalt h = allocHeap(sizeof(struct on_halt));
  153.  
  154.     h->function = f;
  155.     h->argument = arg;
  156.     startCritical;
  157.     h->next = GD->os.on_halt_list;
  158.     GD->os.on_halt_list = h;
  159.     endCritical;
  160.   }
  161. }
  162.  
  163.  
  164. volatile void
  165. Halt(int rval)
  166. { OnHalt h;
  167.   extern int Output;
  168.  
  169.   pl_notrace();                /* avoid recursive tracing */
  170.   Output = 1;                /* reset output stream to user */
  171.  
  172.   if ( !GD->os.halting )
  173.   { GD->os.halting++;            /* avoid recursion */
  174.  
  175.     for(h = GD->os.on_halt_list; h; h = h->next)
  176.       (*h->function)(rval, h->argument);
  177.  
  178.     if ( GD->initialised )
  179.     { fid_t cid = PL_open_foreign_frame();
  180.       predicate_t proc = PL_predicate("$run_at_halt", 0, "system");
  181.       PL_call_predicate(MODULE_system, FALSE, proc, 0);
  182.       PL_discard_foreign_frame(cid);
  183.     }
  184.  
  185. #if defined(__WINDOWS__) || defined(__WIN32__)
  186.     if ( rval != 0 )
  187.       PlMessage("Exit status is %d", rval);
  188. #endif
  189.  
  190.     qlfCleanup();            /* remove errornous .qlf files */
  191.     dieIO();
  192.  
  193.     if ( GD->initialised )
  194.     { fid_t cid = PL_open_foreign_frame();
  195.       predicate_t proc = PL_predicate("unload_all_foreign_libraries", 0,
  196.                       "shlib");
  197.       if ( isDefinedProcedure(proc) )
  198.     PL_call_predicate(MODULE_system, FALSE, proc, 0);
  199.       PL_discard_foreign_frame(cid);
  200.     }
  201.  
  202.     RemoveTemporaryFiles();
  203.   }
  204.  
  205.   exit(rval);
  206.   /*NOTREACHED*/
  207. }
  208.  
  209.         /********************************
  210.         *            OS ERRORS          *
  211.         *********************************/
  212.  
  213. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  214.     char *OsError()
  215.     Return a char *, holding a description of the last OS call error.
  216. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  217.  
  218. char *
  219. OsError(void)
  220. {
  221. #ifdef HAVE_STRERROR
  222. #ifdef __WIN32__
  223.   return strerror(_xos_errno());
  224. #else
  225.   return strerror(errno);
  226. #endif
  227. #else /*HAVE_STRERROR*/
  228. static char errmsg[64];
  229.  
  230. #if unix
  231.   extern int sys_nerr;
  232. #if !EMX
  233.   extern char *sys_errlist[];
  234. #endif
  235.   extern int errno;
  236.  
  237.   if ( errno < sys_nerr )
  238.     return sys_errlist[errno];
  239. #endif
  240.  
  241.   Ssprintf(errmsg, "Unknown Error (%d)", errno);
  242.   return errmsg;
  243. #endif /*HAVE_STRERROR*/
  244. }
  245.  
  246.         /********************************
  247.         *    PROCESS CHARACTERISTICS    *
  248.         *********************************/
  249.  
  250. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  251.     real CpuTime()
  252.  
  253.     Returns a floating point number, representing the amount  of  (user)
  254.     CPU-seconds  used  by the process Prolog is in.  For systems that do
  255.     not allow you to obtain this information  you  may  wish  to  return
  256.     elapsed  time  since Prolog was started, as this function is used to
  257.     by consult/1 and time/1 to determine the amount of CPU time used  to
  258.     consult a file or to execute a query.
  259. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  260.  
  261. #ifndef __WIN32__            /* defined in pl-nt.c */
  262.  
  263. #ifdef HAVE_TIMES
  264. #include <sys/times.h>
  265.  
  266. #if defined(_SC_CLK_TCK)
  267. #define Hz ((int)sysconf(_SC_CLK_TCK))
  268. #else
  269. #ifdef HZ
  270. #  define Hz HZ
  271. #else
  272. #  define Hz 60                /* if nothing better: guess */
  273. #endif
  274. #endif /*_SC_CLK_TCK*/
  275. #endif /*HAVE_TIMES*/
  276.  
  277.  
  278. real
  279. CpuTime(void)
  280. {
  281. #ifdef HAVE_TIMES
  282.   struct tms t;
  283.   static int MTOK_got_hz = FALSE;
  284.   static real MTOK_hz;
  285.  
  286.   if ( !MTOK_got_hz )
  287.   { MTOK_hz = (real) Hz;
  288.     MTOK_got_hz++;
  289.   }
  290.   times(&t);
  291.  
  292.   return (real) t.tms_utime / MTOK_hz;
  293. #endif
  294.  
  295. #if OS2 && EMX
  296.   DATETIME i;
  297.  
  298.   DosGetDateTime((PDATETIME)&i);
  299.   return (((i.hours * 3600) 
  300.                  + (i.minutes * 60) 
  301.          + i.seconds
  302.              + (i.hundredths / 100.0)) - initial_time);
  303. #endif
  304.  
  305. #ifdef HAVE_CLOCK
  306.   return (real) (clock() - clock_wait_ticks) / (real) CLOCKS_PER_SEC;
  307. #endif
  308. }
  309.  
  310. #endif /*__WIN32__*/
  311.  
  312. #ifdef HAVE_CLOCK
  313. void
  314. PL_clock_wait_ticks(long waited)
  315. { clock_wait_ticks += waited;
  316. }
  317. #endif
  318.  
  319.         /********************************
  320.         *       MEMORY MANAGEMENT       *
  321.         *********************************/
  322.  
  323. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  324.     long *Allocate(n)
  325.       long n;
  326.  
  327.     Allocate a memory area of `n' bytes from the operating system.   `n'
  328.     is a long as we need to allocate one uniform array of longs for both
  329.     the  local  stack  and  global  stack,  which  implies  it should be
  330.     possible to allocate at least a few hundred Kbytes.  If  you  cannot
  331.     implement  this  function  you  are in deep trouble.  You either can
  332.     decide to redesign large part of the data representation, or  forget
  333.     about  SWI-Prolog.   Memory  is never returned to the system.  As it
  334.     would only concern small areas,  all  over  SWI-Prolog's  memory  no
  335.     currently  available operating system (I'm aware of) will be able to
  336.     handle it anyway.  THE RETURN VALUE SHOULD BE ROUNDED TO BE A  VALID
  337.     POINTER FOR LONGS AND STRUCTURES AND AT LEAST A MULTIPLE OF 4.
  338. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  339.  
  340. Void
  341. Allocate(long n)
  342. { Void mem = malloc(n);
  343.  
  344.   return (Void) mem;
  345. }
  346.  
  347.  
  348.         /********************************
  349.         *     STRING MANIPULATION    *
  350.         ********************************/
  351.  
  352. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  353. The builtin strcmp() of SunOs is broken on some machines ...
  354. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  355.  
  356. #if sun
  357. int
  358. strcmp(const char *s1, const char *s2)
  359. { while(*s1 && *s1 == *s2)
  360.     s1++, s2++;
  361.  
  362.   return *(const unsigned char *)s1 -
  363.      *(const unsigned char *)s2;
  364. }
  365. #endif
  366.  
  367.  
  368.         /********************************
  369.         *           ARITHMETIC          *
  370.         *********************************/
  371.  
  372. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  373.     long Random()
  374.  
  375.     Return a random number. Used for arithmetic only.
  376. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  377.  
  378. static void
  379. initRandom(void)
  380. { long init = Time();
  381. #ifdef HAVE_SRANDOM
  382.   srandom(init);
  383. #else
  384. #ifdef HAVE_SRAND
  385.   srand(init);
  386. #endif
  387. #endif
  388. }
  389.  
  390. long
  391. Random(void)
  392. #ifdef HAVE_RANDOM
  393.   return random();
  394. #else
  395.   long l = rand();            /* 0<n<2^15-1 */
  396.   
  397.   l ^= rand()<<10;
  398.   l ^= rand()<<20;
  399.  
  400.   return l & (~PLMININT);
  401. #endif
  402. }
  403.  
  404.         /********************************
  405.         *             FILES             *
  406.         *********************************/
  407.  
  408.       /* (Everything you always wanted to know about files ...) */
  409.  
  410. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  411. Generation and administration of temporary files.  Currently  only  used
  412. by  the foreign language linker.  It might be useful to make a predicate
  413. available to the Prolog user based on these functions.  These  functions
  414. are  in  this  module as non-UNIX OS probably don't have getpid() or put
  415. temporaries on /tmp.
  416.  
  417.     atom_t TemporaryFile(const char *id)
  418.  
  419.     The return value of this call is an atom,  whose  string  represents
  420.     the  path  name of a unique file that can be used as temporary file.
  421.     `id' is a char * that can be used to make it easier to identify  the
  422.     file as a specific kind of SWI-Prolog intermediate file.
  423.  
  424.     void RemoveTemporaryFiles()
  425.  
  426.     Remove all temporary files.  This function should be  aware  of  the
  427.     fact  that some of the file names generated by TemporaryFile() might
  428.     not be created at all, or might already have been deleted.
  429. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  430.  
  431. struct tempfile
  432. { atom_t    name;
  433.   TempFile    next;
  434. };                    /* chain of temporary files */
  435.  
  436. #define tmpfile_head (GD->os._tmpfile_head)
  437. #define tmpfile_tail (GD->os._tmpfile_tail)
  438.  
  439. #ifndef DEFTMPDIR
  440. #ifdef __WIN32__
  441. #define DEFTMPDIR "c:/tmp"
  442. #else
  443. #define DEFTMPDIR "/tmp"
  444. #endif
  445. #endif
  446.  
  447. atom_t
  448. TemporaryFile(const char *id)
  449. { char temp[MAXPATHLEN];
  450.   TempFile tf = allocHeap(sizeof(struct tempfile));
  451.   char envbuf[MAXPATHLEN];
  452.   char *tmpdir;
  453.  
  454.   if ( !((tmpdir = getenv3("TEMP", envbuf, sizeof(envbuf))) ||
  455.      (tmpdir = getenv3("TMP",  envbuf, sizeof(envbuf)))) )
  456.     tmpdir = DEFTMPDIR;
  457.  
  458. #if unix
  459. { static int MTOK_temp_counter = 0;
  460.  
  461.   Ssprintf(temp, "%s/pl_%s_%d_%d",
  462.        tmpdir, id, (int) getpid(), MTOK_temp_counter++);
  463. }
  464. #endif
  465.  
  466. #ifdef __WIN32__
  467. { char *tmp;
  468.   static int temp_counter = 0;
  469.  
  470. #ifdef __LCC__
  471.   if ( (tmp = tmpnam(NULL)) )
  472. #else
  473.   if ( (tmp = _tempnam(tmpdir, id)) )
  474. #endif
  475.   { PrologPath(tmp, temp);
  476.   } else
  477.     Ssprintf(temp, "%s/pl_%s_%d", tmpdir, id, temp_counter++);
  478. }
  479. #endif
  480.  
  481. #if EMX
  482.   static int temp_counter = 0;
  483.   char *foo;
  484.  
  485.   if ( (foo = tempnam(".", (const char *)id)) )
  486.   { strcpy(temp, foo);
  487.     free(foo);
  488.   } else
  489.     Ssprintf(temp, "pl_%s_%d_%d", id, getpid(), temp_counter++);
  490. #endif
  491.  
  492. #if tos
  493.   tmpnam(temp);
  494. #endif
  495.  
  496.   tf->name = lookupAtom(temp);
  497.   tf->next = NULL;
  498.   
  499.   startCritical;
  500.   if ( !tmpfile_tail )
  501.   { tmpfile_head = tmpfile_tail = tf;
  502.   } else
  503.   { tmpfile_tail->next = tf;
  504.     tmpfile_tail = tf;
  505.   }
  506.   endCritical;
  507.  
  508.   return tf->name;
  509. }
  510.  
  511. static void
  512. RemoveTemporaryFiles()
  513. { TempFile tf, tf2;  
  514.  
  515.   startCritical;
  516.   for(tf = tmpfile_head; tf; tf = tf2)
  517.   { RemoveFile(stringAtom(tf->name));
  518.     tf2 = tf->next;
  519.     freeHeap(tf, sizeof(struct tempfile));
  520.   }
  521.  
  522.   tmpfile_head = tmpfile_tail = NULL;
  523.   endCritical;
  524. }
  525.  
  526. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  527. Fortunately most C-compilers  are  sold  with  a  library  that  defines
  528. Unix-style  access  to  the  file system.  The standard functions go via
  529. macros to deal with 16-bit machines, but are not  defined  as  functions
  530. here.   Some  more  specific things SWI-Prolog wants to know about files
  531. are defined here:
  532.  
  533.     int  getdtablesize()
  534.  
  535.     SWI-Prolog assumes it can refer to open i/o streams via  read()  and
  536.     write() by small integers, returned by open(). These integers should
  537.     be  in  the range [0, ..., getdtablesize()). If your system does not
  538.     do this you better redefine the Open(), Read() and Write() macros so
  539.     they  do  meet  this  requirement.   Prolog  allocates  a  table  of
  540.     structures with getdtablesize() entries.
  541.  
  542.     long LastModifiedFile(path)
  543.      char *path;
  544.  
  545.     Returns the last time `path' has been modified.  Used by the  source
  546.     file administration to implement make/0.
  547.  
  548.     bool ExistsFile(path)
  549.      char *path;
  550.  
  551.     Succeeds if `path' refers to the pathname of a regular file  (not  a
  552.     directory).
  553.  
  554.     bool AccessFile(path, mode)
  555.      char *path;
  556.      int mode;
  557.  
  558.     Succeeds if `path' is the pathname of an existing file and it can
  559.     be accessed in any of the inclusive or constructed argument `mode'.
  560.  
  561.     bool ExistsDirectory(path)
  562.      char *path;
  563.  
  564.     Succeeds if `path' refers to the pathname  of  a  directory.
  565.  
  566.     bool RemoveFile(path)
  567.      char *path;
  568.  
  569.     Removes a (regular) file from the  file  system.   Returns  TRUE  if
  570.     succesful FALSE otherwise.
  571.  
  572.     bool RenameFile(old, new)
  573.      char *old, *new;
  574.  
  575.     Rename file from name `old' to name `new'. If new already exists, it is
  576.     deleted. Returns TRUE if succesful, FALSE otherwise.
  577.  
  578.     bool OpenStream(stream)
  579.      int stream;
  580.  
  581.     Succeeds if `stream' refers to an open i/o stream.
  582.  
  583.     bool MarkExecutable(path)
  584.      char *path;
  585.  
  586.     Mark `path' as an executable program.  Used by the intermediate code
  587.     compiler and the creation of stand-alone executables.
  588. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  589.  
  590. #ifndef HAVE_GETDTABLESIZE
  591. int
  592. getdtablesize()
  593. {
  594. #ifdef OPEN_MAX
  595.   return OPEN_MAX;
  596. #else
  597. #ifdef _SC_OPEN_MAX            /* POSIX, USG */
  598.   return sysconf(_SC_OPEN_MAX);
  599. #else
  600. #ifdef HAVE_GETRLIMIT
  601. #ifdef HAVE_SYS_RESOURCE_H
  602. #include <sys/resource.h>
  603. #endif
  604. #ifdef RLIMIT_NOFILE
  605.   { struct rlimit rlp;
  606.     getrlimit(RLIMIT_NOFILE,&rlp);
  607.     return (rlp.rlim_cur);
  608.   }
  609. #endif /*RLIMIT_NOFILE*/
  610. #endif /*HAVE_GETRLIMIT*/
  611. #endif /*_SC_OPEN_MAX*/
  612. #endif /*OPEN_MAX*/
  613. }
  614. #endif /*HAVE_GETDTABLESIZE*/
  615.  
  616. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  617. Size of a VM page of memory.  Most BSD machines have this function.  If not,
  618. here are several alternatives ...
  619. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  620.  
  621. #ifndef HAVE_GETPAGESIZE
  622. #ifdef _SC_PAGESIZE
  623. int
  624. getpagesize()
  625. { return sysconf(_SC_PAGESIZE);
  626. }
  627. #else /*_SC_PAGESIZE*/
  628.  
  629. #if hpux
  630. #include <a.out.h>
  631. int
  632. getpagesize()
  633. {  
  634. #ifdef EXEC_PAGESIZE
  635.   return EXEC_PAGESIZE;
  636. #else
  637.   return 4096;                /* not that important */
  638. #endif
  639. }
  640. #endif /*hpux*/
  641. #endif /*_SC_PAGESIZE*/
  642. #endif /*HAVE_GETPAGESIZE*/
  643.  
  644. #if O_HPFS
  645.  
  646. /*  Conversion rules Prolog <-> OS/2 (using HPFS)
  647.     / <-> \
  648.     /x:/ <-> x:\  (embedded drive letter)
  649.     No length restrictions up to MAXPATHLEN, no case conversions.
  650. */
  651.  
  652. char *
  653. PrologPath(char *ospath, char *path)
  654. { char *s = ospath, *p = path;
  655.   int limit = MAXPATHLEN-1;
  656.  
  657.   if (isLetter(s[0]) && s[1] == ':')
  658.   { *p++ = '/';
  659.     *p++ = *s++;
  660.     *p++ = *s++;
  661.     limit -= 3;
  662.   }
  663.   for(; *s && limit; s++, p++, limit--)
  664.     *p = (*s == '\\' ? '/' : makeLower(*s));
  665.   *p = EOS;
  666.  
  667.   return path;
  668. }
  669.  
  670.  
  671. char *
  672. OsPath(const char *plpath, char *path)
  673. { const char *s = plpath, *p = path;
  674.   int limit = MAXPATHLEN-1;
  675.  
  676.   if ( s[0] == '/' && isLetter(s[1]) && s[2] == ':') /* embedded drive letter*/
  677.   { s++;
  678.     *p++ = *s++;
  679.     *p++ = *s++;
  680.     if ( *s != '/' )
  681.       *p++ = '\\';
  682.     limit -= 2;
  683.   }
  684.  
  685.   for(; *s && limit; s++, p++, limit--)
  686.     *p = (*s == '/' ? '\\' : *s);
  687.   if ( p[-1] == '\\' && p > path )
  688.     p--;
  689.   *p = EOS;
  690.  
  691.   return path;
  692. #endif /* O_HPFS */
  693.  
  694. #if unix
  695. char *
  696. PrologPath(const char *p, char *buf)
  697. { strcpy(buf, p);
  698.  
  699.   return buf;
  700. }
  701.  
  702. char *
  703. OsPath(const char *p, char *buf)
  704. { strcpy(buf, p);
  705.  
  706.   return buf;
  707. }
  708. #endif /*unix*/
  709.  
  710. #if O_XOS
  711. char *
  712. PrologPath(const char *p, char *buf)
  713. { _xos_canonical_filename(p, buf);
  714.   if ( !trueFeature(FILE_CASE_FEATURE) )
  715.     strlwr(buf);
  716.  
  717.   return buf;
  718. }
  719.  
  720. char *
  721. OsPath(const char *p, char *buf)
  722. { strcpy(buf, p);
  723.  
  724.   return buf;
  725. }
  726. #endif /* O_XOS */
  727.  
  728. long
  729. LastModifiedFile(char *f)
  730. { char tmp[MAXPATHLEN];
  731.  
  732. #if defined(HAVE_STAT) || defined(__unix__)
  733.   struct stat buf;
  734.  
  735.   if ( statfunc(OsPath(f, tmp), &buf) < 0 )
  736.     return -1;
  737.  
  738.   return (long)buf.st_mtime;
  739. #endif
  740.  
  741. #if tos
  742. #define DAY    (24*60*60L)
  743.   static int msize[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  744.   long t;
  745.   int n;
  746.   struct ffblk buf;
  747.   struct dz
  748.   { unsigned int hour : 5;    /* hour (0-23) */
  749.     unsigned int min  : 6;    /* minute (0-59) */
  750.     unsigned int sec  : 5;    /* seconds in steps of 2 */
  751.     unsigned int year : 7;    /* year (0=1980) */
  752.     unsigned int mon  : 4;    /* month (1-12) */
  753.     unsigned int day  : 5;    /* day (1-31) */
  754.   } *dz;
  755.  
  756.   if ( findfirst(OsPath(f, tmp), &buf, FA_HIDDEN) != 0 )
  757.     return -1;
  758.   dz = (struct dz *) &buf.ff_ftime;
  759.   DEBUG(2, Sdprintf("%d/%d/%d %d:%d:%d\n",
  760.        dz->day, dz->mon, dz->year+1980, dz->hour, dz->min, dz->sec));
  761.  
  762.   t = (10*365+2) * DAY;        /* Start of 1980 */
  763.   for(n=0; n < dz->year; n++)
  764.     t += ((n % 4) == 0 ? 366 : 365) * DAY;
  765.   for(n=1; n < dz->mon; n++)
  766.     t += msize[n+1] * DAY;
  767.   t += (dz->sec * 2) + (dz->min * 60) + (dz->hour *60*60L);
  768.  
  769.   return t;
  770. #endif
  771. }  
  772.  
  773.  
  774. #ifndef F_OK
  775. #define F_OK 0
  776. #endif
  777.  
  778. bool
  779. ExistsFile(const char *path)
  780. { char tmp[MAXPATHLEN];
  781.  
  782. #ifdef HAVE_ACCESS
  783.   if ( access(OsPath(path, tmp), F_OK) == 0 )
  784.     succeed;
  785.   fail;
  786. #else
  787. #if defined(HAVE_STAT) || defined(__unix__)
  788.   struct stat buf;
  789.  
  790.   if ( statfunc(OsPath(path, tmp), &buf) == -1 ||
  791.        (buf.st_mode & S_IFMT) != S_IFREG )
  792.     fail;
  793.   succeed;
  794. #endif
  795.  
  796. #if tos
  797.   struct ffblk buf;
  798.  
  799.   if ( findfirst(OsPath(path, tmp), &buf, FA_HIDDEN) == 0 )
  800.   { DEBUG(2, Sdprintf("%s (%s) exists\n", path, OsPath(path)));
  801.     succeed;
  802.   }
  803.   DEBUG(2, Sdprintf("%s (%s) does not exist\n", path, OsPath(path)));
  804.   fail;
  805. #endif
  806. #endif
  807. }
  808.  
  809. bool
  810. AccessFile(const char *path, int mode)
  811. { char tmp[MAXPATHLEN];
  812. #ifdef HAVE_ACCESS
  813.   int m = 0;
  814.  
  815.   if ( mode == ACCESS_EXIST ) 
  816.     m = F_OK;
  817.   else
  818.   { if ( mode & ACCESS_READ    ) m |= R_OK;
  819.     if ( mode & ACCESS_WRITE   ) m |= W_OK;
  820. #ifdef X_OK
  821.     if ( mode & ACCESS_EXECUTE ) m |= X_OK;
  822. #endif
  823.   }
  824.  
  825.   return access(OsPath(path, tmp), m) == 0 ? TRUE : FALSE;
  826. #endif
  827.  
  828. #ifdef tos
  829.   struct ffblk buf;
  830.  
  831.   if ( findfirst(OsPath(path, tmp), &buf, FA_DIREC|FA_HIDDEN) != 0 )
  832.     fail;            /* does not exists */
  833.   if ( (mode & ACCESS_WRITE) && (buf.ff_attrib & FA_RDONLY) )
  834.     fail;            /* readonly file */
  835.  
  836.   succeed;
  837. #endif
  838. }
  839.  
  840. bool
  841. ExistsDirectory(const char *path)
  842. { char tmp[MAXPATHLEN];
  843.   char *ospath = OsPath(path, tmp);
  844.  
  845. #if defined(HAVE_STAT) || defined(__unix__)
  846.   struct stat buf;
  847.  
  848.   if ( statfunc(ospath, &buf) < 0 )
  849.     fail;
  850.  
  851.   if ( (buf.st_mode & S_IFMT) == S_IFDIR )
  852.     succeed;
  853.  
  854.   fail;
  855. #endif
  856.  
  857. #ifdef tos
  858.   struct ffblk buf;
  859.  
  860.   if ( findfirst(ospath, &buf, FA_DIREC|FA_HIDDEN) == 0 &&
  861.        buf.ff_attrib & FA_DIREC )
  862.     succeed;
  863.   if ( streq(ospath, ".") || streq(ospath, "..") )    /* hack */
  864.     succeed;
  865.   fail;
  866. #endif
  867. }
  868.  
  869.  
  870. long
  871. SizeFile(const char *path)
  872. { char tmp[MAXPATHLEN];
  873.   struct stat buf;
  874.  
  875. #if defined(HAVE_STAT) || defined(__unix__)
  876.   if ( statfunc(OsPath(path, tmp), &buf) == -1 )
  877.     return -1;
  878. #endif
  879.  
  880.   return buf.st_size;
  881. }
  882.  
  883.  
  884. int
  885. RemoveFile(const char *path)
  886. { char tmp[MAXPATHLEN];
  887.  
  888. #ifdef HAVE_REMOVE
  889.   return remove(OsPath(path, tmp)) == 0 ? TRUE : FALSE;
  890. #else
  891.   return unlink(OsPath(path, tmp)) == 0 ? TRUE : FALSE;
  892. #endif
  893. }
  894.  
  895.  
  896. bool
  897. RenameFile(const char *old, const char *new)
  898. { char oldbuf[MAXPATHLEN];
  899.   char newbuf[MAXPATHLEN];
  900.   char *osold, *osnew;
  901.  
  902.   osold = OsPath(old, oldbuf);
  903.   osnew = OsPath(new, newbuf);
  904.  
  905. #ifdef HAVE_RENAME
  906.   return rename(osold, osnew) == 0 ? TRUE : FALSE;
  907. #else
  908. { int rval;
  909.  
  910.   unlink(osnew);
  911.   if ( (rval = link(osold, osnew)) == 0 
  912.        && (rval = unlink(osold)) != 0)
  913.     unlink(osnew);
  914.  
  915.   if ( rval == 0 )
  916.     succeed;
  917.  
  918.   fail;
  919. }
  920. #endif /*HAVE_RENAME*/
  921. }
  922.  
  923. bool
  924. SameFile(const char *f1, const char *f2)
  925. { if ( trueFeature(FILE_CASE_FEATURE) )
  926.   { if ( streq(f1, f2) )
  927.       succeed;
  928.   } else
  929.   { if ( stricmp(f1, f2) == 0 )
  930.       succeed;
  931.   }
  932.  
  933. #ifdef unix                /* doesn't work on most not Unix's */
  934.   { struct stat buf1;
  935.     struct stat buf2;
  936.     char tmp[MAXPATHLEN];
  937.  
  938.     if ( statfunc(OsPath(f1, tmp), &buf1) != 0 ||
  939.      statfunc(OsPath(f2, tmp), &buf2) != 0 )
  940.       fail;
  941.     if ( buf1.st_ino == buf2.st_ino && buf1.st_dev == buf2.st_dev )
  942.       succeed;
  943.   }
  944. #endif
  945. #ifdef O_XOS
  946.   { char p1[MAXPATHLEN];
  947.     char p2[MAXPATHLEN];
  948.  
  949.     _xos_limited_os_filename(f1, p1);
  950.     _xos_limited_os_filename(f2, p2);
  951.     if ( trueFeature(FILE_CASE_FEATURE) )
  952.     { if ( streq(p1, p2) )
  953.     succeed;
  954.     } else
  955.     { if ( stricmp(p1, p2) == 0 )
  956.     succeed;
  957.     }
  958.   }
  959. #endif /*O_XOS*/
  960.     /* Amazing! There is no simple way to check two files for identity. */
  961.     /* stat() and fstat() both return dummy values for inode and device. */
  962.     /* this is fine as OS'es not supporting symbolic links don't need this */
  963.  
  964.   fail;
  965. }
  966.  
  967.  
  968. bool
  969. MarkExecutable(const char *name)
  970. {
  971. #if (defined(HAVE_STAT) && defined(HAVE_CHMOD)) || defined(__unix__)
  972.   struct stat buf;
  973.   int um;
  974.  
  975.   um = umask(0777);
  976.   umask(um);
  977.   if ( statfunc(name, &buf) == -1 )
  978.   { term_t file = PL_new_term_ref();
  979.  
  980.     PL_put_atom_chars(file, name);
  981.     PL_error(NULL, 0, OsError(), ERR_FILE_OPERATION,
  982.          ATOM_stat, ATOM_file, file);
  983.   }
  984.  
  985.   if ( (buf.st_mode & 0111) == (~um & 0111) )
  986.     succeed;
  987.  
  988.   buf.st_mode |= 0111 & ~um;
  989.   if ( chmod(name, buf.st_mode) == -1 )
  990.   { term_t file = PL_new_term_ref();
  991.  
  992.     PL_put_atom_chars(file, name);
  993.     PL_error(NULL, 0, OsError(), ERR_FILE_OPERATION,
  994.          ATOM_chmod, ATOM_file, file);
  995.   }
  996. #endif /* defined(HAVE_STAT) && defined(HAVE_CHMOD) */
  997.  
  998.   succeed;
  999. }
  1000.  
  1001. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1002.     char *AbsoluteFile(const char *file, char *path)
  1003.  
  1004.     Expand a file specification to a system-wide unique  description  of
  1005.     the  file  that can be passed to the file functions that take a path
  1006.     as argument.  Path should refer to the same file, regardless of  the
  1007.     current  working  directory.   On  Unix absolute file names are used
  1008.     for this purpose.
  1009.  
  1010.     This  function  is  based  on  a  similar  (primitive)  function  in
  1011.     Edinburgh C-Prolog.
  1012.  
  1013.     char *BaseName(path)
  1014.      char *path;
  1015.  
  1016.     Return the basic file name for a file having path `path'.
  1017.  
  1018.     char *DirName(const char *path, char *dir)
  1019.     
  1020.     Return the directory name for a file having path `path'.
  1021. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1022.  
  1023. #if defined(HAVE_SYMLINKS) && (defined(HAVE_STAT) || defined(__unix__))
  1024. #define O_CANONISE_DIRS
  1025.  
  1026. struct canonical_dir
  1027. { char *    name;            /* name of directory */
  1028.   char *    canonical;        /* canonical name of directory */
  1029.   dev_t        device;            /* device number */
  1030.   ino_t        inode;            /* inode number */
  1031.   CanonicalDir  next;            /* next in chain */
  1032. };
  1033.  
  1034. #define canonical_dirlist (GD->os._canonical_dirlist)
  1035.  
  1036. forwards char   *canoniseDir(char *);
  1037. #endif /*O_CANONISE_DIRS*/
  1038.  
  1039. #define CWDdir    (LD->os._CWDdir)    /* current directory */
  1040. #define CWDlen    (LD->os._CWDlen)    /* strlen(CWDdir) */
  1041.  
  1042. static void
  1043. initExpand(void)
  1044. #ifdef O_CANONISE_DIRS
  1045.   char *dir;
  1046.   char *cpaths;
  1047. #endif
  1048.  
  1049.   CWDdir = NULL;
  1050.   CWDlen = 0;
  1051.  
  1052. #ifdef O_CANONISE_DIRS
  1053. { char envbuf[MAXPATHLEN];
  1054.  
  1055.   if ( (cpaths = getenv3("CANONICAL_PATHS", envbuf, sizeof(envbuf))) )
  1056.   { char buf[MAXPATHLEN];
  1057.  
  1058.     while(*cpaths)
  1059.     { char *e;
  1060.  
  1061.       if ( (e = strchr(cpaths, ':')) )
  1062.       { int l = e-cpaths;
  1063.  
  1064.     strncpy(buf, cpaths, l);
  1065.     buf[l] = EOS;
  1066.     cpaths += l+1;
  1067.     canoniseDir(buf);
  1068.       } else
  1069.       { canoniseDir(cpaths);
  1070.     break;
  1071.       }
  1072.     }
  1073.   }
  1074.  
  1075.   if ( (dir = getenv3("HOME", envbuf, sizeof(envbuf))) ) canoniseDir(dir);
  1076.   if ( (dir = getenv3("PWD",  envbuf, sizeof(envbuf))) ) canoniseDir(dir);
  1077.   if ( (dir = getenv3("CWD",  envbuf, sizeof(envbuf))) ) canoniseDir(dir);
  1078. }
  1079. #endif
  1080. }
  1081.  
  1082. #ifdef O_CANONISE_DIRS
  1083.  
  1084. static char *
  1085. canoniseDir(char *path)
  1086. { CanonicalDir d;
  1087.   struct stat buf;
  1088.   char tmp[MAXPATHLEN];
  1089.  
  1090.   DEBUG(1, Sdprintf("canoniseDir(%s) --> ", path));
  1091.  
  1092.   for(d = canonical_dirlist; d; d = d->next)
  1093.   { if ( streq(d->name, path) )
  1094.     { if ( d->name != d->canonical )
  1095.     strcpy(path, d->canonical);
  1096.  
  1097.       DEBUG(1, Sdprintf("(lookup) %s\n", path));
  1098.       return path;
  1099.     }
  1100.   }
  1101.  
  1102.   if ( statfunc(OsPath(path, tmp), &buf) == 0 )
  1103.   { CanonicalDir dn = allocHeap(sizeof(struct canonical_dir));
  1104.     char dirname[MAXPATHLEN];
  1105.     char *e = path + strlen(path);
  1106.  
  1107.     dn->next   = canonical_dirlist;
  1108.     dn->name   = store_string(path);
  1109.     dn->inode  = buf.st_ino;
  1110.     dn->device = buf.st_dev;
  1111.  
  1112.     do
  1113.     { strncpy(dirname, path, e-path);
  1114.       dirname[e-path] = EOS;
  1115.       if ( statfunc(OsPath(dirname, tmp), &buf) < 0 )
  1116.     break;
  1117.  
  1118.       DEBUG(2, Sdprintf("Checking %s (dev=%d,ino=%d)\n",
  1119.             dirname, buf.st_dev, buf.st_ino));
  1120.  
  1121.       for(d = canonical_dirlist; d; d = d->next)
  1122.       { if ( d->inode == buf.st_ino && d->device == buf.st_dev )
  1123.     { canonical_dirlist = dn;
  1124.  
  1125.       DEBUG(2, Sdprintf("Hit with %s (dev=%d,ino=%d)\n",
  1126.                 d->canonical, d->device, d->inode));
  1127.  
  1128.       strcpy(dirname, d->canonical);
  1129.       strcat(dirname, e);
  1130.       strcpy(path, dirname);
  1131.       dn->canonical = store_string(path);
  1132.       DEBUG(1, Sdprintf("(replace) %s\n", path));
  1133.       return path;
  1134.     }
  1135.       }
  1136.  
  1137.       for(e--; *e != '/' && e > path + 1; e-- )
  1138.     ;
  1139.  
  1140.     } while( e > path );
  1141.  
  1142.     dn->canonical = dn->name;
  1143.     canonical_dirlist = dn;
  1144.  
  1145.     DEBUG(1, Sdprintf("(new, existing) %s\n", path));
  1146.     return path;
  1147.   }
  1148.  
  1149.   DEBUG(1, Sdprintf("(nonexisting) %s\n", path));
  1150.   return path;
  1151. }
  1152.  
  1153. #else
  1154.  
  1155. #define canoniseDir(d)
  1156.  
  1157. #endif /*O_CANONISE_DIRS*/
  1158.  
  1159.  
  1160. static char *
  1161. canoniseFileName(char *path)
  1162. { char *out = path, *in = path;
  1163.   char *osave[100];
  1164.   int  osavep = 0;
  1165.  
  1166.   while( in[0] == '/' && in[1] == '.' && in[2] == '.' && in[3] == '/' )
  1167.     in += 3;
  1168.   if ( in[0] == '/' )
  1169.     *out++ = '/';
  1170.   osave[osavep++] = out;
  1171.  
  1172.   while(*in)
  1173.   { if (*in == '/')
  1174.     {
  1175.     again:
  1176.       if ( *in )
  1177.       { while( in[1] == '/' )
  1178.       in++;
  1179.     if ( in[1] == '.' && (in[2] == '/' || in[2] == EOS) )
  1180.     { in += 2;
  1181.       goto again;
  1182.     }
  1183.     if ( in[1] == '.' && in[2] == '.' &&
  1184.          (in[3] == '/' || in[3] == EOS) && osavep > 0 )
  1185.     { out = osave[--osavep];
  1186.       in += 3;
  1187.       goto again;
  1188.     }
  1189.       }
  1190.       if ( *in )
  1191.     in++;
  1192.       if ( out > path && out[-1] != '/' )
  1193.     *out++ = '/';
  1194.       osave[osavep++] = out;
  1195.     } else
  1196.       *out++ = *in++;
  1197.   }
  1198.   *out++ = *in++;
  1199.  
  1200.   return path;
  1201. }
  1202.  
  1203.  
  1204. char *
  1205. canonisePath(char *path)
  1206. { if ( !trueFeature(FILE_CASE_FEATURE) )
  1207.     strlwr(path);
  1208.  
  1209.   canoniseFileName(path);
  1210.  
  1211. #ifdef O_CANONISE_DIRS
  1212. { char *e;
  1213.   char dirname[MAXPATHLEN];
  1214.  
  1215.   e = path + strlen(path) - 1;
  1216.   for( ; *e != '/' && e > path; e-- )
  1217.     ;
  1218.   strncpy(dirname, path, e-path);
  1219.   dirname[e-path] = EOS;
  1220.   canoniseDir(dirname);
  1221.   strcat(dirname, e);
  1222.   strcpy(path, dirname);
  1223. }
  1224. #endif
  1225.  
  1226.   return path;
  1227. }
  1228.  
  1229.  
  1230. static char *
  1231. takeWord(const char **string, char *wrd)
  1232. { const char *s = *string;
  1233.   char *q = wrd;
  1234.   int left = MAXPATHLEN-1;
  1235.  
  1236.   while( isAlpha(*s) || *s == '_' )
  1237.   { if ( --left < 0 )
  1238.     { PL_error(NULL, 0, NULL, ERR_REPRESENTATION,
  1239.            ATOM_max_variable_length);
  1240.       return NULL;
  1241.     }
  1242.     *q++ = *s++;
  1243.   }
  1244.   *q = EOS;
  1245.   
  1246.   *string = s;
  1247.   return wrd;
  1248. }
  1249.  
  1250.  
  1251. bool
  1252. expandVars(const char *pattern, char *expanded)
  1253. { int size = 0;
  1254.   char c;
  1255.   char word[MAXPATHLEN];
  1256.  
  1257.   if ( *pattern == '~' )
  1258.   { char *user;
  1259.     char *value;
  1260.     int l;
  1261.  
  1262.     pattern++;
  1263.     user = takeWord(&pattern, word);
  1264.     if ( user[0] == EOS )        /* ~/bla */
  1265.     {
  1266. #ifdef O_XOS
  1267.       value = _xos_home();
  1268. #else /*O_XOS*/
  1269.       if ( !(value = GD->os.myhome) )
  1270.       { char envbuf[MAXPATHLEN];
  1271.  
  1272.     if ( (value = getenv3("HOME", envbuf, sizeof(envbuf))) )
  1273.     { value = GD->os.myhome = store_string(PrologPath(value, word));
  1274.     } else
  1275.     { value = GD->os.myhome = "/";
  1276.     }
  1277.       }
  1278. #endif /*O_XOS*/
  1279.     } else                /* ~fred */
  1280. #ifdef HAVE_GETPWNAM
  1281.     { struct passwd *pwent;
  1282.  
  1283.       if ( GD->os.fred && !streq(GD->os.fred, user) )
  1284.       { value = GD->os.fredshome;
  1285.       } else
  1286.       { if ( !(pwent = getpwnam(user)) )
  1287.     { if ( fileerrors )
  1288.       { term_t name = PL_new_term_ref();
  1289.  
  1290.         PL_put_atom_chars(name, user);
  1291.         PL_error(NULL, 0, NULL, ERR_EXISTENCE, ATOM_user, name);
  1292.       }
  1293.       fail;
  1294.     }
  1295.     if ( GD->os.fred )
  1296.       remove_string(GD->os.fred);
  1297.     if ( GD->os.fredshome )
  1298.       remove_string(GD->os.fredshome);
  1299.     
  1300.     GD->os.fred = store_string(user);
  1301.     value = GD->os.fredshome = store_string(pwent->pw_dir);
  1302.       }
  1303.     }      
  1304. #else
  1305.     { if ( fileerrors )
  1306.     PL_error(NULL, 0, NULL, ERR_NOTIMPLEMENTED, PL_new_atom("user_info"));
  1307.  
  1308.       fail;
  1309.     }
  1310. #endif
  1311.     size += (l = (int) strlen(value));
  1312.     if ( size >= MAXPATHLEN )
  1313.       return PL_error(NULL, 0, NULL, ERR_REPRESENTATION, ATOM_max_path_length);
  1314.     strcpy(expanded, value);
  1315.     expanded += l;
  1316.   }
  1317.  
  1318.   for( ;; )
  1319.   { switch( c = *pattern++ )
  1320.     { case EOS:
  1321.     break;
  1322.       case '$':
  1323.     { char envbuf[MAXPATHLEN];
  1324.       char *var = takeWord(&pattern, word);
  1325.       char *value = getenv3(var, envbuf, sizeof(envbuf));
  1326.       int l;
  1327.  
  1328.       if ( value == (char *) NULL )
  1329.       { if ( fileerrors )
  1330.         { term_t name = PL_new_term_ref();
  1331.  
  1332.           PL_put_atom_chars(name, var);
  1333.           PL_error(NULL, 0, NULL, ERR_EXISTENCE, ATOM_variable, name);
  1334.         }
  1335.  
  1336.         fail;
  1337.       }
  1338.       size += (l = (int)strlen(value));
  1339.       if ( size >= MAXPATHLEN )
  1340.         return PL_error(NULL, 0, NULL, ERR_REPRESENTATION,
  1341.                 ATOM_max_path_length);
  1342.       strcpy(expanded, value);
  1343.       expanded += l;
  1344.  
  1345.       continue;
  1346.     }
  1347.       default:
  1348.     if ( ++size >= MAXPATHLEN )
  1349.       return PL_error(NULL, 0, NULL, ERR_REPRESENTATION,
  1350.               ATOM_max_path_length);
  1351.     *expanded++ = c;
  1352.  
  1353.     continue;
  1354.     }
  1355.     break;
  1356.   }
  1357.  
  1358.   if ( ++size >= MAXPATHLEN )
  1359.     return PL_error(NULL, 0, NULL, ERR_REPRESENTATION,
  1360.             ATOM_max_path_length);
  1361.   *expanded = EOS;
  1362.  
  1363.   succeed;
  1364. }
  1365.  
  1366.  
  1367. static int
  1368. ExpandFile(const char *pattern, char **vector)
  1369. { char expanded[MAXPATHLEN];
  1370.   int matches = 0;
  1371.  
  1372.   if ( !expandVars(pattern, expanded) )
  1373.     return -1;
  1374.   
  1375.   vector[matches++] = store_string(expanded);
  1376.  
  1377.   return matches;
  1378. }
  1379.  
  1380.  
  1381. char *
  1382. ExpandOneFile(const char *spec, char *file)
  1383. { char *vector[256];
  1384.   int size;
  1385.  
  1386.   switch( (size=ExpandFile(spec, vector)) )
  1387.   { case -1:
  1388.       return NULL;
  1389.     case 0:
  1390.     { term_t tmp = PL_new_term_ref();
  1391.       
  1392.       PL_put_atom_chars(tmp, spec);
  1393.       PL_error(NULL, 0, "no match", ERR_EXISTENCE, ATOM_file, tmp);
  1394.  
  1395.       return NULL;
  1396.     }
  1397.     case 1:
  1398.       strcpy(file, vector[0]);
  1399.       remove_string(vector[0]);
  1400.       return file;
  1401.     default:
  1402.     { term_t tmp = PL_new_term_ref();
  1403.       int n;
  1404.       
  1405.       for(n=0; n<size; n++)
  1406.     remove_string(vector[n]);
  1407.       PL_put_atom_chars(tmp, spec);
  1408.       PL_error(NULL, 0, "ambiguous", ERR_EXISTENCE, ATOM_file, tmp);
  1409.  
  1410.       return NULL;
  1411.     }
  1412.   }
  1413. }
  1414.  
  1415.  
  1416. #ifdef O_HASDRIVES
  1417.  
  1418. int
  1419. IsAbsolutePath(const char *p)        /* /d:/ or d:/ */
  1420. { if ( p[0] == '/' && p[2] == ':' && isLetter(p[1]) &&
  1421.        (p[3] == '/' || p[3] == '\0') )
  1422.     succeed;
  1423.  
  1424.   if ( p[1] == ':' && isLetter(p[0]) && (p[2] == '/' || p[2] == '\0') )
  1425.     succeed;
  1426.  
  1427.   fail;
  1428. }
  1429.  
  1430.  
  1431. static inline int
  1432. isDriveRelativePath(const char *p)    /* '/...' */
  1433. { return p[0] == '/' && !IsAbsolutePath(p);
  1434. }
  1435.  
  1436. #ifdef __WIN32__
  1437. #undef mkdir
  1438. #include <direct.h>
  1439. #define mkdir _xos_mkdir
  1440. #endif
  1441.  
  1442. static int
  1443. GetCurrentDriveLetter()
  1444. {
  1445. #ifdef OS2
  1446.   return _getdrive();
  1447. #endif
  1448. #ifdef __WIN32__
  1449.   return _getdrive() + 'a' - 1;
  1450. #endif
  1451. #ifdef __WATCOMC__
  1452.   { unsigned drive;
  1453.     _dos_getdrive(&drive);
  1454.     return = 'a' + drive - 1;
  1455.   }
  1456. #endif
  1457. }
  1458.  
  1459. #else /*O_HASDRIVES*/
  1460.  
  1461. int
  1462. IsAbsolutePath(const char *p)
  1463. { return p[0] == '/';
  1464. }
  1465.  
  1466. #endif /*O_HASDRIVES*/
  1467.  
  1468. #define isRelativePath(p) ( p[0] == '.' )
  1469.  
  1470.  
  1471. char *
  1472. AbsoluteFile(const char *spec, char *path)
  1473. { char tmp[MAXPATHLEN];
  1474.   char buf[MAXPATHLEN];
  1475.   char *file;  
  1476.  
  1477.   PrologPath(spec, buf);
  1478.   if ( !(file = ExpandOneFile(buf, tmp)) )
  1479.     return (char *) NULL;
  1480.  
  1481.   if ( IsAbsolutePath(file) )
  1482.   { strcpy(path, file);
  1483.  
  1484.     return canonisePath(path);
  1485.   }
  1486.  
  1487. #ifdef O_HASDRIVES
  1488.   if ( isDriveRelativePath(file) )    /* /something  --> d:/something */
  1489.   { if ((strlen(file) + 3) > MAXPATHLEN)
  1490.     { PL_error(NULL, 0, NULL, ERR_REPRESENTATION, ATOM_max_path_length);
  1491.       return (char *) NULL;
  1492.     }
  1493.     path[0] = GetCurrentDriveLetter();
  1494.     path[1] = ':';
  1495.     strcpy(&path[2], file);
  1496.     return canonisePath(path);
  1497.   }
  1498. #endif /*O_HASDRIVES*/
  1499.  
  1500.   if ( CWDlen == 0 )
  1501.   { char buf[MAXPATHLEN];
  1502.     char *rval;
  1503.  
  1504. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1505. On SunOs, getcwd() is using popen() to read the output of /bin/pwd.  This
  1506. is slow and appears not to cooperate with profile/3.  getwd() is supposed
  1507. to be implemented directly.  What about other Unixes?
  1508. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1509.  
  1510. #if defined(HAVE_GETWD) && (defined(__sun__) || !defined(HAVE_GETCWD))
  1511.     rval = getwd(buf);
  1512. #else
  1513.     rval = getcwd(buf, MAXPATHLEN);
  1514. #endif
  1515.     if ( !rval )
  1516.     { term_t tmp = PL_new_term_ref();
  1517.  
  1518.       PL_put_atom(tmp, ATOM_dot);
  1519.       PL_error(NULL, 0, OsError(), ERR_FILE_OPERATION,
  1520.            ATOM_getcwd, ATOM_directory, tmp);
  1521.     }
  1522.  
  1523.     canonisePath(buf);
  1524.     CWDlen = strlen(buf);
  1525.     buf[CWDlen++] = '/';
  1526.     buf[CWDlen] = EOS;
  1527.     
  1528.     if ( CWDdir )
  1529.       remove_string(CWDdir);
  1530.     CWDdir = store_string(buf);
  1531.   }
  1532.  
  1533.   if ( (CWDlen + strlen(file) + 1) >= MAXPATHLEN )
  1534.   { PL_error(NULL, 0, NULL, ERR_REPRESENTATION, ATOM_max_path_length);
  1535.     return (char *) NULL;
  1536.   }
  1537.   
  1538.   strcpy(path, CWDdir);
  1539.   if ( file[0] != EOS )
  1540.     strcpy(&path[CWDlen], file);
  1541.   if ( strchr(file, '.') || strchr(file, '/') )
  1542.     return canonisePath(path);
  1543.   else
  1544.     return path;
  1545. }
  1546.  
  1547.  
  1548. char *
  1549. BaseName(register char *f)
  1550. { register char *base;
  1551.  
  1552.   for(base = f; *f; f++)
  1553.   { if (*f == '/')
  1554.       base = f+1;
  1555.   }
  1556.  
  1557.   return base;
  1558. }
  1559.  
  1560.  
  1561. char *
  1562. DirName(const char *f, char *dir)
  1563. { const char *base, *p;
  1564.  
  1565.   for(base = p = f; *p; p++)
  1566.   { if (*p == '/' && p[1] != EOS )
  1567.       base = p+1;
  1568.   }
  1569.   strncpy(dir, f, base-f);
  1570.   dir[base-f] = EOS;
  1571.   
  1572.   return dir;
  1573. }
  1574.  
  1575.  
  1576. char *
  1577. ReadLink(const char *f, char *buf)
  1578. {
  1579. #ifdef HAVE_READLINK
  1580.   int n;
  1581.  
  1582.   if ( (n=readlink(f, buf, MAXPATHLEN-1)) > 0 )
  1583.   { buf[n] = EOS;
  1584.     return buf;
  1585.   }
  1586. #endif
  1587.  
  1588.   return NULL;
  1589. }
  1590.  
  1591.  
  1592. static char *
  1593. DeRefLink1(const char *f, char *lbuf)
  1594. { char buf[MAXPATHLEN];
  1595.   char *l;
  1596.  
  1597.   if ( (l=ReadLink(f, buf)) )
  1598.   { if ( l[0] == '/' )            /* absolute path */
  1599.     { strcpy(lbuf, buf);
  1600.       return lbuf;
  1601.     } else
  1602.     { char *q;
  1603.  
  1604.       strcpy(lbuf, f);
  1605.       q = &lbuf[strlen(lbuf)];
  1606.       while(q>lbuf && q[-1] != '/')
  1607.     q--;
  1608.       strcpy(q, l);
  1609.  
  1610.       canoniseFileName(lbuf);
  1611.  
  1612.       return lbuf;
  1613.     }
  1614.   }
  1615.  
  1616.   return NULL;
  1617. }
  1618.  
  1619.  
  1620. char *
  1621. DeRefLink(const    char *link, char *buf)
  1622. { char tmp[MAXPATHLEN];
  1623.   char *f;
  1624.   int n = 20;                /* avoid loop! */
  1625.  
  1626.   while((f=DeRefLink1(link, tmp)) && n-- > 0)
  1627.     link = f;
  1628.  
  1629.   if ( n > 0 )
  1630.   { strcpy(buf, link);
  1631.     return buf;
  1632.   } else
  1633.     return NULL;
  1634. }
  1635.  
  1636.  
  1637.  
  1638. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1639.     bool ChDir(path)
  1640.      char *path;
  1641.  
  1642.     Change the current working directory to `path'.  File names may depend
  1643.     on `path'.
  1644. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1645.  
  1646. bool
  1647. ChDir(const char *path)
  1648. { char ospath[MAXPATHLEN];
  1649.   char tmp[MAXPATHLEN];
  1650.  
  1651.   OsPath(path, ospath);
  1652.  
  1653.   if ( path[0] == EOS || streq(path, ".") ||
  1654.        (CWDdir && streq(path, CWDdir)) )
  1655.     succeed;
  1656.  
  1657.   AbsoluteFile(path, tmp);
  1658.  
  1659.   if ( chdir(ospath) == 0 )
  1660.   { int len;
  1661.  
  1662.     len = strlen(tmp);
  1663.     if ( len == 0 || tmp[len-1] != '/' )
  1664.     { tmp[len++] = '/';
  1665.       tmp[len] = EOS;
  1666.     }
  1667.     CWDlen = len;
  1668.     if ( CWDdir )
  1669.       remove_string(CWDdir);
  1670.     CWDdir = store_string(tmp);
  1671.  
  1672.     succeed;
  1673.   }
  1674.  
  1675.   fail;
  1676. }
  1677.  
  1678.  
  1679.  
  1680.         /********************************
  1681.         *        TIME CONVERSION        *
  1682.         *********************************/
  1683.  
  1684. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1685.     struct tm *LocalTime(time)
  1686.           long *time;
  1687.  
  1688.     Convert time in Unix internal form (seconds since Jan 1 1970) into a
  1689.     structure providing easier access to the time.
  1690.  
  1691.     For non-Unix systems: struct time is supposed  to  look  like  this.
  1692.     Move  This  definition to pl-os.h and write the conversion functions
  1693.     here.
  1694.  
  1695.     struct tm {
  1696.     int    tm_sec;        / * second in the minute (0-59)* /
  1697.     int    tm_min;        / * minute in the hour (0-59) * /
  1698.     int    tm_hour;    / * hour of the day (0-23) * /
  1699.     int    tm_mday;    / * day of the month (1-31) * /
  1700.     int    tm_mon;        / * month of the year (1-12) * /
  1701.     int    tm_year;    / * year (0 = 1900) * /
  1702.     int    tm_wday;    / * day in the week (1-7, 1 = sunday) * /
  1703.     int    tm_yday;    / * day in the year (0-365) * /
  1704.     int    tm_isdst;    / * daylight saving time info * /
  1705.     };
  1706.  
  1707.     long Time()
  1708.  
  1709.     Return time in seconds after Jan 1 1970 (Unix' time notion).
  1710. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1711.  
  1712. struct tm *
  1713. LocalTime(long int *t)
  1714. { return localtime((const time_t *) t);
  1715. }
  1716.  
  1717.  
  1718. static long
  1719. Time(void)
  1720. { return (long)time((time_t *) NULL);
  1721. }
  1722.  
  1723.  
  1724. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1725.             TERMINAL IO MANIPULATION
  1726.  
  1727. ResetStdin()
  1728.     Clear the Sinput buffer after a saved state.  Only necessary
  1729.     if O_SAVE is defined.
  1730.  
  1731. PushTty()
  1732.     Push the tty to the specified state.
  1733.  
  1734. PopTty()
  1735.     Restore the tty state.
  1736. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1737.  
  1738. static void
  1739. ResetStdin()
  1740. { Sinput->limitp = Sinput->bufp = Sinput->buffer;
  1741.   if ( !GD->os.org_terminal.read )
  1742.     GD->os.org_terminal = *Sinput->functions;
  1743. }
  1744.  
  1745. static int
  1746. Swrite_protocol(void *handle, char *buf, int size)
  1747. { int rval;
  1748. #ifdef HAVE_CLOCK
  1749.   long oldclock = clock();
  1750. #endif
  1751.  
  1752.   protocol(buf, size);
  1753.  
  1754.   rval = (*GD->os.org_terminal.write)(handle, buf, size);
  1755.  
  1756. #ifdef HAVE_CLOCK
  1757.   clock_wait_ticks += clock() - oldclock;
  1758. #endif
  1759.  
  1760.   return rval;
  1761. }
  1762.  
  1763. int
  1764. Sread_terminal(void *handle, char *buf, int size)
  1765. { long h     = (long)handle;
  1766.   atom_t sfn = source_file_name;        /* save over call-back */
  1767.   int    sln = source_line_no;
  1768.   int     fd = (int)h;
  1769.  
  1770.   if ( GD->os.prompt_next && ttymode != TTY_RAW )
  1771.   { Putf("%s", PrologPrompt());
  1772.     
  1773.     GD->os.prompt_next = FALSE;
  1774.   }
  1775.  
  1776.   pl_ttyflush();
  1777.   PL_dispatch(fd, PL_DISPATCH_WAIT);
  1778.   size = (*GD->os.org_terminal.read)(handle, buf, size);
  1779.  
  1780.   if ( size == 0 )            /* end-of-file */
  1781.   { if ( fd == 0 )
  1782.     { Sclearerr(Sinput);
  1783.       GD->os.prompt_next = TRUE;
  1784.     }
  1785.   } else if ( size > 0 && buf[size-1] == '\n' )
  1786.     GD->os.prompt_next = TRUE;
  1787.  
  1788.   source_line_no   = sln;
  1789.   source_file_name = sfn;
  1790.  
  1791.   return size;
  1792. }
  1793.  
  1794. void
  1795. ResetTty()
  1796. { startCritical;
  1797.   ResetStdin();
  1798.  
  1799.   if ( !GD->os.iofunctions.read )
  1800.   { GD->os.iofunctions       = *Sinput->functions;
  1801.     GD->os.iofunctions.read  = Sread_terminal;
  1802.     GD->os.iofunctions.write = Swrite_protocol;
  1803.  
  1804.     Sinput->functions  = 
  1805.     Soutput->functions = 
  1806.     Serror->functions  = &GD->os.iofunctions;
  1807.   }
  1808.   GD->os.prompt_next = TRUE;
  1809.   endCritical;
  1810. }
  1811.  
  1812. #ifdef O_HAVE_TERMIO            /* sys/termios.h or sys/termio.h */
  1813.  
  1814. #ifndef HAVE_TCSETATTR
  1815. #ifndef NO_SYS_IOCTL_H_WITH_SYS_TERMIOS_H
  1816. #include <sys/ioctl.h>
  1817. #endif
  1818. #ifndef TIOCGETA
  1819. #define TIOCGETA TCGETA
  1820. #endif
  1821. #endif
  1822.  
  1823. bool
  1824. PushTty(ttybuf *buf, int mode)
  1825. { struct termios tio;
  1826.  
  1827.   buf->mode = ttymode;
  1828.   ttymode = mode;
  1829.  
  1830.   if ( GD->cmdline.notty )
  1831.     succeed;
  1832.  
  1833. #ifdef HAVE_TCSETATTR 
  1834.   if ( tcgetattr(0, &buf->tab) )    /* save the old one */
  1835.     fail;
  1836. #else
  1837.   if ( ioctl(0, TIOCGETA, &buf->tab) )    /* save the old one */
  1838.     fail;
  1839. #endif
  1840.  
  1841.   tio = buf->tab;
  1842.  
  1843.   switch( mode )
  1844.   { case TTY_RAW:
  1845. #if defined(HAVE_TCSETATTR) && defined(HAVE_CFMAKERAW)
  1846.     cfmakeraw(&tio);
  1847.     tio.c_oflag = buf->tab.c_oflag;    /* donot change output modes */
  1848.     tio.c_lflag |= ISIG;
  1849. #else
  1850.     tio.c_lflag &= ~(ECHO|ICANON);
  1851.     tio.c_cc[VTIME] = 0, tio.c_cc[VMIN] = 1;
  1852. #endif
  1853.     break;
  1854.     case TTY_OUTPUT:
  1855.     tio.c_oflag |= (OPOST|ONLCR);
  1856.         break;
  1857.     case TTY_SAVE:
  1858.         succeed;
  1859.     default:
  1860.     sysError("Unknown PushTty() mode: %d", mode);
  1861.     /*NOTREACHED*/
  1862.   }
  1863.  
  1864. #ifdef HAVE_TCSETATTR
  1865.   if ( tcsetattr(0, TCSANOW, &tio) != 0 )
  1866.   { static int MTOK_warned;            /* MT-OK */
  1867.  
  1868.     if ( !MTOK_warned++ )
  1869.       warning("Failed to set terminal: %s", OsError());
  1870.   }
  1871. #else
  1872. #ifdef TIOCSETAW
  1873.   ioctl(0, TIOCSETAW, &tio);
  1874. #else
  1875.   ioctl(0, TCSETAW, &tio);
  1876.   ioctl(0, TCXONC, (void *)1);
  1877. #endif
  1878. #endif
  1879.  
  1880.   succeed;
  1881. }
  1882.  
  1883.  
  1884. bool
  1885. PopTty(ttybuf *buf)
  1886. { ttymode = buf->mode;
  1887.  
  1888.   if ( GD->cmdline.notty )
  1889.     succeed;
  1890.  
  1891. #ifdef HAVE_TCSETATTR
  1892.   tcsetattr(0, TCSANOW, &buf->tab);
  1893. #else
  1894. #ifdef TIOCSETA
  1895.   ioctl(0, TIOCSETA, &buf->tab);
  1896. #else
  1897.   ioctl(0, TCSETA, &buf->tab);
  1898.   ioctl(0, TCXONC, (void *)1);
  1899. #endif
  1900. #endif
  1901.  
  1902.   succeed;
  1903. }
  1904.  
  1905. #else /* O_HAVE_TERMIO */
  1906.  
  1907. #ifdef HAVE_SGTTYB
  1908.  
  1909. bool
  1910. PushTty(ttybuf *buf, int mode)
  1911. { struct sgttyb tio;
  1912.  
  1913.   buf->mode = ttymode;
  1914.   ttymode = mode;
  1915.  
  1916.   if ( GD->cmdline.notty )
  1917.     succeed;
  1918.  
  1919.   if ( ioctl(0, TIOCGETP, &buf->tab) )  /* save the old one */
  1920.     fail;
  1921.   tio = buf->tab;
  1922.  
  1923.   switch( mode )
  1924.     { case TTY_RAW:
  1925.     tio.sg_flags |= CBREAK;
  1926.     tio.sg_flags &= ~ECHO;
  1927.     break;
  1928.       case TTY_OUTPUT:
  1929.     tio.sg_flags |= (CRMOD);
  1930.     break;
  1931.       case TTY_SAVE:
  1932.     succeed;
  1933.       default:
  1934.     sysError("Unknown PushTty() mode: %d", mode);
  1935.     /*NOTREACHED*/
  1936.       }
  1937.   
  1938.   
  1939.   ioctl(0, TIOCSETP,  &tio);
  1940.   ioctl(0, TIOCSTART, NULL);
  1941.  
  1942.   succeed;
  1943. }
  1944.  
  1945.  
  1946. bool
  1947. PopTty(ttybuf *buf)
  1948. { ttymode = buf->mode;
  1949.  
  1950.   if ( GD->cmdline.notty )
  1951.     succeed;
  1952.  
  1953.   ioctl(0, TIOCSETP,  &buf->tab);
  1954.   ioctl(0, TIOCSTART, NULL);
  1955.  
  1956.   succeed;
  1957. }
  1958.  
  1959. #else /*HAVE_SGTTYB*/
  1960.  
  1961. bool
  1962. PushTty(buf, mode)
  1963. ttybuf *buf;
  1964. int mode;
  1965. { buf->mode = ttymode;
  1966.   ttymode = mode;
  1967.  
  1968.   succeed;
  1969. }
  1970.  
  1971.  
  1972. bool
  1973. PopTty(buf)
  1974. ttybuf *buf;
  1975. { ttymode = buf->mode;
  1976.   if ( ttymode != TTY_RAW )
  1977.     GD->os.prompt_next = TRUE;
  1978.  
  1979.   succeed;
  1980. }
  1981.  
  1982. #endif /*HAVE_SGTTYB*/
  1983. #endif /*O_HAVE_TERMIO*/
  1984.  
  1985.  
  1986.         /********************************
  1987.         *      ENVIRONMENT CONTROL      *
  1988.         *********************************/
  1989.  
  1990. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1991. Simple  library  to  manipulate  the  Unix  environment.   The  modified
  1992. environment  will  be  passed  to  child  processes  and the can also be
  1993. requested via getenv/2 from Prolog.  Functions
  1994.  
  1995.     char *Setenv(name, value)
  1996.          char *name, *value;
  1997.     
  1998.     Set the Unix environment variable with name `name'.   If  it  exists
  1999.     its  value  is  changed, otherwise a new entry in the environment is
  2000.     created.  The return value is a pointer to the old value, or NULL if
  2001.     the variable is new.
  2002.  
  2003.     char *Unsetenv(name)
  2004.          char *name;
  2005.  
  2006.     Delete a variable from the environment.  Return  value  is  the  old
  2007.     value, or NULL if the variable did not exist.
  2008. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2009.  
  2010. #ifndef __WIN32__
  2011. char *
  2012. getenv3(const char *name, char *buf, int len)
  2013. { char *s = getenv(name);
  2014.  
  2015.   if ( s && strlen(s) < len )
  2016.   { strcpy(buf, s);
  2017.  
  2018.     return buf;
  2019.   }
  2020.  
  2021.   return NULL;
  2022. }
  2023.  
  2024. int
  2025. getenvl(const char *name)
  2026. { char *s;
  2027.  
  2028.   if ( (s = getenv(name)) )
  2029.     return strlen(s);
  2030.  
  2031.   return -1;
  2032. }
  2033. #endif
  2034.  
  2035. #if HAVE_PUTENV
  2036.  
  2037. int
  2038. Setenv(char *name, char *value)
  2039. { char *buf = alloca(strlen(name) + strlen(value) + 2);
  2040.  
  2041.   if ( buf )
  2042.   { Ssprintf(buf, "%s=%s", name, value);
  2043.  
  2044.     if ( putenv(store_string(buf)) < 0 )
  2045.       return PL_error("setenv", 2, NULL, ERR_NOMEM);
  2046.   } else
  2047.     return PL_error("setenv", 2, NULL, ERR_NOMEM);
  2048.  
  2049.   succeed;
  2050. }
  2051.  
  2052. int
  2053. Unsetenv(char *name)
  2054. { return Setenv(name, "");
  2055. }
  2056.  
  2057. static void
  2058. initEnviron()
  2059. {
  2060. }
  2061.  
  2062. #else /*HAVE_PUTENV*/
  2063.  
  2064. #ifdef tos
  2065. char **environ;
  2066. #else
  2067. extern char **environ;        /* Unix predefined environment */
  2068. #endif
  2069.  
  2070. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2071. Grow the environment array by one and return the (possibly  moved)  base
  2072. pointer to the new environment.
  2073. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2074.  
  2075. forwards char    **growEnviron(char**, int);
  2076. forwards char    *matchName(char *, char *);
  2077. forwards void    setEntry(char **, char *, char *);
  2078.  
  2079. static char **
  2080. growEnviron(char **e, int amount)
  2081. { static int filled;
  2082.   static int size = -1;
  2083.  
  2084.   if ( amount == 0 )            /* reset after a dump */
  2085.   { size = -1;
  2086.     return e;
  2087.   }
  2088.  
  2089.   if ( size < 0 )
  2090.   { register char **env, **e1, **e2;
  2091.  
  2092.     for(e1=e, filled=0; *e1; e1++, filled++)
  2093.       ;
  2094.     size = ROUND(filled+10+amount, 32);
  2095.     env = (char **)malloc(size * sizeof(char *));
  2096.     for ( e1=e, e2=env; *e1; *e2++ = *e1++ )
  2097.       ;
  2098.     *e2 = (char *) NULL;
  2099.     filled += amount;
  2100.  
  2101.     return env;
  2102.   }
  2103.  
  2104.   filled += amount;
  2105.   if ( filled + 1 > size )
  2106.   { register char **env, **e1, **e2;
  2107.   
  2108.     size += 32;
  2109.     env = (char **)realloc(e, size * sizeof(char *));
  2110.     for ( e1=e, e2=env; *e1; *e2++ = *e1++ )
  2111.       ;
  2112.     *e2 = (char *) NULL;
  2113.     
  2114.     return env;
  2115.   }
  2116.  
  2117.   return e;
  2118. }
  2119.  
  2120.  
  2121. static void
  2122. initEnviron(void)
  2123. { growEnviron(environ, 0);
  2124. }
  2125.  
  2126.  
  2127. static char *
  2128. matchName(register char *e, register char *name)
  2129. { while( *name && *e == *name )
  2130.     e++, name++;
  2131.  
  2132.   if ( (*e == '=' || *e == EOS) && *name == EOS )
  2133.     return (*e == '=' ? e+1 : e);
  2134.  
  2135.   return (char *) NULL;
  2136. }
  2137.  
  2138.  
  2139. static void
  2140. setEntry(char **e, char *name, char *value)
  2141. { int l = (int)strlen(name);
  2142.  
  2143.   *e = (char *) malloc(l + strlen(value) + 2);
  2144.   strcpy(*e, name);
  2145.   e[0][l++] = '=';
  2146.   strcpy(&e[0][l], value);
  2147. }
  2148.  
  2149.   
  2150. char *
  2151. Setenv(char *name, char *value)
  2152. { char **e;
  2153.   char *v;
  2154.   int n;
  2155.  
  2156.   for(n=0, e=environ; *e; e++, n++)
  2157.   { if ( (v=matchName(*e, name)) != NULL )
  2158.     { if ( !streq(v, value) )
  2159.         setEntry(e, name, value);
  2160.       return v;
  2161.     }
  2162.   }
  2163.   environ = growEnviron(environ, 1);
  2164.   setEntry(&environ[n], name, value);
  2165.   environ[n+1] = (char *) NULL;
  2166.  
  2167.   return (char *) NULL;
  2168. }
  2169.  
  2170.  
  2171. char *
  2172. Unsetenv(char *name)
  2173. { char **e;
  2174.   char *v;
  2175.   int n;
  2176.  
  2177.   for(n=0, e=environ; *e; e++, n++)
  2178.   { if ( (v=matchName(*e, name)) != NULL )
  2179.     { environ = growEnviron(environ, -1);
  2180.       e = &environ[n];
  2181.       do
  2182.       { e[0] = e[1];
  2183.         e++;
  2184.       } while(*e);
  2185.  
  2186.       return v;
  2187.     }
  2188.   }
  2189.  
  2190.   return (char *) NULL;
  2191. }
  2192.  
  2193. #endif /*HAVE_PUTENV*/
  2194.  
  2195.         /********************************
  2196.         *       SYSTEM PROCESSES        *
  2197.         *********************************/
  2198.  
  2199. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2200.     int System(command)
  2201.     char *command;
  2202.  
  2203.     Invoke a command on the operating system.  The return value  is  the
  2204.     exit  status  of  the  command.   Return  value  0 implies succesful
  2205.     completion. If you are not running Unix your C-library might provide
  2206.     an alternative.
  2207. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2208.  
  2209. #ifdef unix
  2210. #define SPECIFIC_SYSTEM 1
  2211. #if defined(HAVE_SYS_RESOURCE_H)
  2212. #include <sys/resource.h>
  2213. #endif
  2214. #if defined(HAVE_SYS_WAIT_H) || defined(UNION_WAIT)
  2215. #include <sys/wait.h>
  2216. #endif
  2217.  
  2218. #ifdef UNION_WAIT
  2219.  
  2220. #define wait_t union wait
  2221.  
  2222. #ifndef WEXITSTATUS
  2223. #define WEXITSTATUS(s) ((s).w_status)
  2224. #endif
  2225. #ifndef WTERMSIG
  2226. #define WTERMSIG(s) ((s).w_status)
  2227. #endif
  2228.  
  2229. #else /*UNION_WAIT*/
  2230.  
  2231. #define wait_t int
  2232.  
  2233. #ifndef WEXITSTATUS
  2234. # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
  2235. #endif
  2236. #ifndef WIFEXITED
  2237. # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
  2238. #endif
  2239.  
  2240. #endif /*UNION_WAIT*/
  2241.  
  2242. int
  2243. System(char *cmd)
  2244. { int pid;
  2245.   char *shell = "/bin/sh";
  2246.   int rval;
  2247.   void (*old_int)();
  2248.   void (*old_stop)();
  2249.  
  2250.   Setenv("PROLOGCHILD", "yes");
  2251.  
  2252.   if ( (pid = vfork()) == -1 )
  2253.   { return PL_error("shell", 2, OsError(), ERR_SYSCALL, ATOM_fork);
  2254.   } else if ( pid == 0 )        /* The child */
  2255.   { int i;
  2256.     int fdmax = getdtablesize();
  2257.  
  2258.     for(i = 3; i < fdmax; i++)
  2259.       close(i);
  2260.     stopItimer();
  2261.  
  2262.     execl(shell, BaseName(shell), "-c", cmd, (char *)0);
  2263.     fatalError("Failed to execute %s: %s", shell, OsError());
  2264.     fail;
  2265.     /*NOTREACHED*/
  2266.   } else
  2267.   { wait_t status;            /* the parent */
  2268.     int n;
  2269.  
  2270.     old_int  = signal(SIGINT,  SIG_IGN);
  2271. #ifdef SIGTSTP
  2272.     old_stop = signal(SIGTSTP, SIG_DFL);
  2273. #endif /* SIGTSTP */
  2274.  
  2275.     while((n = Wait(&status)) != -1 && n != pid);
  2276.     if (n == -1)
  2277.     { term_t tmp = PL_new_term_ref();
  2278.       
  2279.       PL_put_atom_chars(tmp, cmd);
  2280.       PL_error("shell", 2, NULL, ERR_SHELL_FAILED, tmp);
  2281.  
  2282.       rval = 1;
  2283.     } else if (WIFEXITED(status))
  2284.     { rval = WEXITSTATUS(status);
  2285. #ifdef WIFSIGNALED
  2286.     } else if (WIFSIGNALED(status))
  2287.     { term_t tmp = PL_new_term_ref();
  2288.       int sig = WTERMSIG(status);
  2289.       
  2290.       PL_put_atom_chars(tmp, cmd);
  2291.       PL_error("shell", 2, NULL, ERR_SHELL_SIGNALLED, tmp, sig);
  2292.       rval = 1;
  2293. #endif
  2294.     } else
  2295.     { rval = 1;                /* make gcc happy */
  2296.       fatalError("Unknown return code from wait(3)");
  2297.       /*NOTREACHED*/
  2298.     }
  2299.   }
  2300.  
  2301.   signal(SIGINT,  old_int);        /* restore signal handlers */
  2302. #ifdef SIGTSTP
  2303.   signal(SIGTSTP, old_stop);
  2304. #endif /* SIGTSTP */
  2305.  
  2306.   return rval;
  2307. }
  2308. #endif /* unix */
  2309.  
  2310. #ifdef tos
  2311. #define SPECIFIC_SYSTEM 1
  2312. #include <aes.h>
  2313.  
  2314. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2315. The routine system_via_shell() has been written by Tom Demeijer.  Thanks!
  2316. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2317.  
  2318. #define _SHELL_P ((long *)0x4f6L)
  2319. #define SHELL_OK (do_sys != 0)
  2320.  
  2321. int cdecl (*do_sys)(const char *cmd); /* Parameter on stack ! */
  2322.  
  2323. static int
  2324. system_via_shell(const char *cmd)
  2325. { long oldssp;
  2326.  
  2327.   oldssp = Super((void *)0L);
  2328.   do_sys = (void (*))*_SHELL_P;
  2329.   Super((void *)oldssp);
  2330.  
  2331.   if(cmd==NULL && SHELL_OK)
  2332.     return 0;
  2333.  
  2334.   if (SHELL_OK)
  2335.     return do_sys(cmd);
  2336.  
  2337.   return -1;
  2338. }
  2339.  
  2340. int
  2341. System(command)
  2342. char *command;
  2343. { char     tmp[MANIPULATION];
  2344.   char       path[MAXPATHLEN];
  2345.   char       *cmd_path;
  2346.   COMMAND  commandline;
  2347.   char       *s, *q;
  2348.   int       status, l;
  2349.   char       *cmd = command;
  2350.  
  2351.   if ( (status = system_via_shell(command)) != -1 )
  2352.   { Sprintf("\033e");        /* get cursor back */
  2353.  
  2354.     return status;
  2355.   }
  2356.  
  2357.     /* get the name of the executable and store in path */
  2358.   for(s=path; *cmd != EOS && !isBlank(*cmd); *s++ = *cmd++)
  2359.     ;
  2360.   *s = EOS;
  2361.   if ( !(cmd_path = Which(path, tmp)) )
  2362.   { warning("%s: command not found", path);
  2363.     return 1;
  2364.   }
  2365.  
  2366.     /* copy the command in commandline */
  2367.   while( isBlank(*cmd) )
  2368.     cmd++;
  2369.  
  2370.   for(l = 0, s = cmd, q = commandline.command_tail; *s && l <= 126; s++ )
  2371.   { if ( *s != '\'' )
  2372.     { *q++ = (*s == '/' ? '\\' : *s);
  2373.       l++;
  2374.     }
  2375.   }
  2376.   commandline.length = l;
  2377.   *q = EOS;
  2378.   
  2379.     /* execute the command */
  2380.   if ( (status = (int) Pexec(0, OsPath(cmd_path), &commandline, NULL)) < 0 )
  2381.   { warning("Failed to execute %s: %s", command, OsError());
  2382.     return 1;
  2383.   }
  2384.  
  2385.     /* clean up after a graphics application */
  2386.   if ( strpostfix(cmd_path, ".prg") || strpostfix(cmd_path, ".tos") )
  2387.   { graf_mouse(M_OFF, NULL);        /* get rid of the mouse */
  2388.     Sprintf("\033e\033E");        /* clear screen and get cursor */
  2389.   }  
  2390.  
  2391.   return status;
  2392. }
  2393. #endif
  2394.  
  2395. #ifdef HAVE_WINEXEC            /* Windows 3.1 */
  2396. #define SPECIFIC_SYSTEM 1
  2397.  
  2398. int
  2399. System(char *command)
  2400. { char *msg;
  2401.   int rval = WinExec(command, SW_SHOWNORMAL);
  2402.  
  2403.   if ( rval < 32 )
  2404.   { switch( rval )
  2405.     { case 0:    msg = "Not enough memory"; break;
  2406.       case 2:    msg = "File not found"; break;
  2407.       case 3:    msg = "No path"; break;
  2408.       case 5:    msg = "Unknown error"; break;
  2409.       case 6:    msg = "Lib requires separate data segment"; break;
  2410.       case 8:    msg = "Not enough memory"; break;
  2411.       case 10:    msg = "Incompatible Windows version"; break;
  2412.       case 11:    msg = "Bad executable file"; break;
  2413.       case 12:    msg = "Incompatible operating system"; break;
  2414.       case 13:    msg = "MS-DOS 4.0 executable"; break;
  2415.       case 14:    msg = "Unknown executable file type"; break;
  2416.       case 15:    msg = "Real-mode application"; break;
  2417.       case 16:    msg = "Cannot start multiple copies"; break;
  2418.       case 19:    msg = "Executable is compressed"; break;
  2419.       case 20:    msg = "Invalid DLL"; break;
  2420.       case 21:    msg = "Application is 32-bits"; break;
  2421.       default:    msg = "Unknown error";
  2422.     }
  2423.  
  2424.     warning("Could not start %s: error %d (%s)",
  2425.         command, rval, msg);
  2426.     return 1;
  2427.   }
  2428.  
  2429.   return 0;
  2430. }
  2431. #endif
  2432.  
  2433.  
  2434. #ifdef __WIN32__
  2435. #define SPECIFIC_SYSTEM 1
  2436.  
  2437.                     /* definition in pl-nt.c */
  2438. #endif
  2439.  
  2440. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2441. Nothing special is needed.  Just hope the C-library defines system().
  2442. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2443.  
  2444. #ifndef SPECIFIC_SYSTEM
  2445.  
  2446. int
  2447. System(command)
  2448. char *command;
  2449. { return system(command);
  2450. }
  2451.  
  2452. #endif
  2453.  
  2454. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2455.     char *Symbols(char *buf)
  2456.  
  2457.     Return the path name of the executable of SWI-Prolog. Used by the -c
  2458.     compiler to generate the #!<path> header line and by the incremental
  2459.     loader, who gives this path to ld, using ld -A <path>.
  2460. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2461.  
  2462. #ifndef __WIN32__            /* Win32 version in pl-nt.c */
  2463.  
  2464. char *
  2465. Symbols(char *buffer)
  2466. { char *file;
  2467.   char buf[MAXPATHLEN];
  2468.   char tmp[MAXPATHLEN];
  2469.  
  2470.   PrologPath(GD->cmdline.argv[0], buf);
  2471.   file = Which(buf, tmp);
  2472.  
  2473. #if __unix__                /* argv[0] can be an #! script! */
  2474.   if ( file )
  2475.   { int n, fd;
  2476.     char buf[MAXPATHLEN];
  2477.  
  2478.     if ( (fd = open(file, O_RDONLY)) < 0 )
  2479.     { warning("Cannot open %s: %s", file, OsError());
  2480.       return file;
  2481.     }
  2482.  
  2483.     if ( (n=read(fd, buf, sizeof(buf)-1)) > 0 )
  2484.     { close(fd);
  2485.  
  2486.       buf[n] = EOS;
  2487.       if ( strncmp(buf, "#!", 2) == 0 )
  2488.       { char *s = &buf[2], *q;
  2489.     while(*s && isBlank(*s))
  2490.       s++;
  2491.     for(q=s; *q && !isBlank(*q); q++)
  2492.       ;
  2493.     *q = EOS;
  2494.  
  2495.     strcpy(buffer, s);
  2496.  
  2497.     return buffer;
  2498.       }
  2499.     }
  2500.  
  2501.     close(fd);
  2502.   }
  2503. #endif /*__unix__*/
  2504.  
  2505.   if ( file )
  2506.     strcpy(buffer, file);
  2507.   else
  2508.     strcpy(buffer, buf);
  2509.  
  2510.   return buffer;
  2511. }
  2512. #endif /*__WIN32__*/
  2513.  
  2514.  
  2515. #if unix
  2516. static char *
  2517. okToExec(const char *s)
  2518. { struct stat stbuff;
  2519.  
  2520.   if (statfunc(s, &stbuff) == 0 &&            /* stat it */
  2521.      (stbuff.st_mode & S_IFMT) == S_IFREG &&    /* check for file */
  2522.      access(s, X_OK) == 0)            /* can be executed? */
  2523.     return (char *)s;
  2524.   else
  2525.     return (char *) NULL;
  2526. }
  2527. #define PATHSEP    ':'
  2528. #endif /* unix */
  2529.  
  2530. #ifdef tos
  2531. #define EXEC_EXTENSIONS { ".ttp", ".prg", NULL }
  2532. #define PATHSEP ','
  2533. #endif
  2534.  
  2535. #if defined(OS2) || defined(__DOS__) || defined(__WINDOWS__) || defined(__WIN32__)
  2536. #define EXEC_EXTENSIONS { ".exe", ".com", ".bat", ".cmd", NULL }
  2537. #define PATHSEP ';'
  2538. #endif
  2539.  
  2540. #ifdef EXEC_EXTENSIONS
  2541.  
  2542. static char *
  2543. okToExec(const char *s)
  2544. { static char *extensions[] = EXEC_EXTENSIONS;
  2545.   static char **ext;
  2546.  
  2547.   DEBUG(2, Sdprintf("Checking %s\n", s));
  2548.   for(ext = extensions; *ext; ext++)
  2549.     if ( stripostfix(s, *ext) )
  2550.       return ExistsFile(s) ? (char *)s : (char *) NULL;
  2551.  
  2552.   for(ext = extensions; *ext; ext++)
  2553.   { static char path[MAXPATHLEN];
  2554.  
  2555.     strcpy(path, s);
  2556.     strcat(path, *ext);
  2557.     if ( ExistsFile(path) )
  2558.       return path;
  2559.   }
  2560.  
  2561.   return (char *) NULL;
  2562. }
  2563. #endif /*EXEC_EXTENSIONS*/
  2564.  
  2565. char *
  2566. Which(const char *program, char *fullname)
  2567. { char *path, *dir;
  2568.   char *e;
  2569.  
  2570.   if ( IsAbsolutePath(program) ||
  2571. #if OS2 && EMX
  2572.        isDriveRelativePath(program) ||
  2573. #endif /* OS2 */
  2574.        isRelativePath(program) ||
  2575.        strchr(program, '/') )
  2576.   { if ( (e = okToExec(program)) != NULL )
  2577.     { strcpy(fullname, e);
  2578.       
  2579.       return fullname;
  2580.     }
  2581.  
  2582.     return NULL;
  2583.   }
  2584.  
  2585. #if OS2 && EMX
  2586.   if ((e = okToExec(program)) != NULL)
  2587.   {
  2588.     getcwd(fullname, MAXPATHLEN);
  2589.     strcat(fullname, "/");
  2590.     strcat(fullname, e);
  2591.     return fullname;
  2592.   }
  2593. #endif /* OS2 */
  2594.   if  ((path = getenv("PATH") ) == 0)
  2595.     path = DEFAULT_PATH;
  2596.  
  2597.   while(*path)
  2598.   { if ( *path == PATHSEP )
  2599.     { if ( (e = okToExec(program)) != NULL)
  2600.       { strcpy(fullname, e);
  2601.  
  2602.         return fullname;
  2603.       } else
  2604.         path++;                /* fix by Ron Hess (hess@sco.com) */
  2605.     } else
  2606.     { char tmp[MAXPATHLEN];
  2607.  
  2608.       for(dir = fullname; *path && *path != PATHSEP; *dir++ = *path++)
  2609.     ;
  2610.       if (*path)
  2611.     path++;                /* skip : */
  2612.       if (strlen(fullname) + strlen(program)+2 > MAXPATHLEN)
  2613.         continue;
  2614.       *dir++ = '/';
  2615.       *dir = EOS;
  2616.       strcpy(dir, program);
  2617.       if ( (e = okToExec(OsPath(fullname, tmp))) != NULL )
  2618.     return e;
  2619.     }
  2620.   }
  2621.  
  2622.   return NULL;
  2623. }
  2624.  
  2625.  
  2626. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2627.     void Pause(time)
  2628.      real time;
  2629.  
  2630.     Suspend execution `time' seconds.   Time  is  given  as  a  floating
  2631.     point,  expressing  the  time  to sleep in seconds.
  2632. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2633.  
  2634. #ifdef HAVE_SELECT
  2635.  
  2636. void
  2637. Pause(real time)
  2638. { struct timeval timeout;
  2639.  
  2640.   if ( time <= 0.0 )
  2641.     return;
  2642.  
  2643.   if ( time < 60.0 )        /* select() is expensive. Does it make sense */
  2644.   { timeout.tv_sec = (int) time;
  2645.     timeout.tv_usec = (int)(time * 1000000) % 1000000;
  2646.     select(32, NULL, NULL, NULL, &timeout);
  2647.   } else
  2648.     sleep( (int)(time+0.5) );
  2649. }
  2650.  
  2651. #else /*!HAVE_SELECT*/
  2652. #ifdef HAVE_DOSSLEEP
  2653.  
  2654. void                            /* a millisecond granualrity. */
  2655. Pause(time)                     /* the EMX function sleep uses a seconds */
  2656. real time;                      /* granularity only. */
  2657. {                               /* the select() trick does not work at all. */
  2658.   if ( time <= 0.0 )
  2659.     return;
  2660.  
  2661.   DosSleep((ULONG)(time * 1000));
  2662. }
  2663.  
  2664. #else /*HAVE_DOSSLEEP*/
  2665. #ifdef HAVE_SLEEP
  2666.  
  2667. void
  2668. Pause(real t)
  2669. { if ( t <= 0.5 )
  2670.     return;
  2671.  
  2672.   sleep((int)(t + 0.5));
  2673. }
  2674. #else /*HAVE_SLEEP*/
  2675. #ifdef HAVE_DELAY
  2676.  
  2677. void
  2678. Pause(real t)
  2679. { delay((int)(t * 1000));
  2680. }
  2681.  
  2682. #endif /*HAVE_DELAY*/
  2683. #endif /*HAVE_SLEEP*/
  2684. #endif /*HAVE_DOSSLEEP*/
  2685. #endif /*HAVE_SELECT*/
  2686.  
  2687. #if tos
  2688. void
  2689. Pause(t)
  2690. real t;
  2691. { long wait = (long)(t * 200.0);
  2692.   long start_tick = clock();
  2693.   long end_tick = wait + start_tick;
  2694.  
  2695.   while( clock() < end_tick )
  2696.   { if ( kbhit() )
  2697.     { wait_ticks += clock() - start_tick;
  2698.       start_tick = clock();
  2699.       TtyAddChar(getch());
  2700.     }
  2701.   }
  2702.  
  2703.   wait_ticks += end_tick - start_tick;
  2704. }
  2705. #endif
  2706.